/** Fichier implémentant une classe implémentant un point de l'espace.
 *
 * Ce fichier contient l'implémentation de la classe Coord.
 *
 * @file Coord.cpp
 * @include Coord.hpp Algebre/Coord.hpp
 *
 * @author Jean-Marc Comby
 * @author Romain Dequesne
 * @author Johan "Solo" Luisier
 * @date 19 Mars 2004
 */

#include "Coord.hpp"
#include "Utils/Erreur.hpp"
#include <iostream>

#include <limits>

#include <cmath>

using namespace std;

/**
 * Constructeur par défaut, qui crée le point d'origine \f$\left( 0,
 * 0, 0 \right)\f$.
 */
Coord::Coord(const bool& origine)
    : VecteurBase( 3 )
{
    if ( origine )
    {
        StatutInterne = Coord::Nul;
        for ( unsigned i( 0u ); i < 3u; i++ )
            operator()( i ) = 0.;
    }
    else
    {
        StatutInterne = Coord::Infini;
        for ( unsigned i( 0u ); i < 3u; i++ )
            operator()( i ) = numeric_limits< double >::max();
    }
}


/**
 * Constructeur usuel, permet d'initialiser les trois coordonnées du
 * point.
 *
 * @param a coordonnée selon Ox.
 * @param b coordonnée selon Oy.
 * @param c coordonnée selon Oz.
 */
Coord::Coord(const double &a, const double &b, const double &c)
    : VecteurBase( 3 )
    , StatutInterne( Coord::Initialise )
{
    operator()( 0 ) = a;
    operator()( 1 ) = b;
    operator()( 2 ) = c;
}

/**
 * Constructeur de copie, permettant de dupliquer une instance de
 * Coord.
 *
 * @param c instance à copier.
 */
Coord::Coord(const Coord &c)
    : VecteurBase( c )
    , StatutInterne( c.StatutInterne )
{}

/**
 * Constructeur de "copie", qui permet de promouvoir un objet issu du
 * résultat d'une opération en un Coord.
 *
 * @param[in] vecBoost objet analogue à vector< double >.
 */
Coord::Coord( const VecteurBase& vecBoost )
    : VecteurBase( vecBoost )
{}

/**
 * Le destructeur n'a rien de spécial à faire.
 */
Coord::~Coord()
{}

/**
 * Permet d'accéder à la composante i de la coordonée.
 *
 * @param[in] i numéro de la composante.
 *
 * @return une référence constante sur la composante i.
 */
const double& Coord::at( const unsigned& i ) const
{
    return operator()( i );
}

/**
 * Permet de récupérer la valeur de la coordonnée \f$x\f$.
 *
 * @return une référence constante sur coord[0].
 */
const double& Coord::x() const
{
    return at( 0 );
}

/**
 * Permet de récupérer la valeur de la coordonnée \f$y\f$.
 *
 * @return une référence constante sur coord[1].
 */
const double& Coord::y() const
{
    return at( 1 );
}

/**
 * Permet de récupérer la valeur de la coordonnée \f$z\f$.
 *
 * @return une référence constante sur coord[2].
 */
const double& Coord::z() const
{
    return at( 2 );
}

/**
 * Permet de récupérer la valeur du rayon (au carré) du point vu en
 * coordonnées sphériques.
 *
 * @return la valeur du carré de la norme de l'instance.
 */
double Coord::r2() const
{
    return x()*x() + y()*y() + z()*z();
}

/**
 * Permet de récupérer la valeur du rayon du point.
 *
 * Utilise Coord::r2().
 *
 * @return la valeur de la norme de l'instance.
 */
double Coord::r() const
{
    return sqrt( r2() );
}

/**
 * Permet de modifier la valeur de la coordonnée \f$x\f$.
 *
 * @param x nouvelle valeur de la coordonnée \f$x\f$.
 */
void Coord::setX(const double& x)
{
    operator()( 0 ) = x;
}

/**
 * Permet de modifier la valeur de la coordonnée \f$y\f$.
 *
 * @param y nouvelle valeur de la coordonnée \f$y\f$.
 */
void Coord::setY(const double& y)
{
    operator()( 1 ) = y;
}

/**
 * Permet de modifier la valeur de la coordonnée \f$z\f$.
 *
 * @param z nouvelle valeur de la coordonnée \f$z\f$.
 */
void Coord::setZ(const double& z)
{
    operator()( 2 ) = z;
}

/**
 * Réalise l'affichage des coordonnées de l'instance sur la sortie
 * standard, suivit par un retour à la ligne.
 *
 * Utiliser préférentiellement la fonction amie operator<<.
 */
void Coord::affiche()
{
    cout << *this << endl;
}

/**
 * Opérateur de comparaison entre deux instances de Coord. Deux points
 * sont égaux si toutes leurs composantes sont égales entre elles. Une
 * tolérance de \f$10^{-7}\f$ est appliquée ici.
 *
 * @param coord point à comparer avec l'instance courante.
 * @return \e true si les deux points sont identiques, \e false sinon.
 */
bool Coord::operator==(const Coord &coord) const
{
    if ( estNul() && coord.estNul() )
        return true;
    else if ( estInfini() && coord.estInfini() )
        return true;

    return ( abs( x() - coord.x() ) < 1e-7 &&
             abs( y() - coord.y() ) < 1e-7 &&
             abs( z() - coord.z() ) < 1e-7 );
}

/**
 * Opérateur d'inégalité, qui permet de déterminer si deux instances
 * de Coord sont différentes. Une tolérance de \f$10^{-7}\f$ est
 * appliquée ici.
 *
 * @param coord point à comparer avec l'instance courante.
 * @return \e false si les deux points sont identiques, \e true sinon.
 */
bool Coord::operator!=(const Coord &coord) const
{
    if ( StatutInterne == coord.StatutInterne )
        return false;

    return ( abs( x() - coord.x() ) > 1e-7 ||
             abs( y() - coord.y() ) > 1e-7 ||
             abs( z() - coord.z() ) > 1e-7 );
}

/**
 * Opérateur permettant de réaliser une opération de parité sur
 * l'instance courante. On renverse toutes les coordonnées en
 * multipliant une copie de l'instance courante par -1.
 *
 * @return une nouvelle instance de Coord, dont les coordonées sont
 * l'inverse des coordonnées de l'instance courante.
 */
Coord Coord::operator-() const
{
    if ( StatutInterne == Coord::Nul || StatutInterne == Coord::Infini )
        return *this;

    Coord tmp( *this );
    return tmp *= -1.;
}

/**
 * Opérateur d'affectation, permettant de copier le contenu d'une
 * instance dans une autre.
 *
 * On n'effectue la copie uniquement si la source est différente de
 * l'instancee courante. (Pour éviter a = a).
 *
 * @param c instance que l'on veut affecter à l'instance courante.
 * @return une référence sur l'instance courante modifiée.
 */
Coord &Coord::operator=(const Coord &c)
{
    if ( &c != this ) // on regarde d abord s il sont different
    {
        this -> assign( c );
    }
    return *this;
}

/**
 * Opérateur d'affectation, permettant de copier promouvoir une de
 * type vector< double > en Coord.
 *
 * On n'effectue la copie uniquement si la source est différente de
 * l'instancee courante. (Pour éviter a = a).
 *
 * @param[in] vecBoost instance que l'on veut affecter à l'instance courante.
 *
 * @return une référence sur l'instance courante modifiée.
 */
Coord &Coord::operator=(const VecteurBase &vecBoost)
{
    if ( &vecBoost != this ) // on regarde d abord s il sont different
    {
        this -> assign( vecBoost );
    }
    return *this;
}

/**
 * Permet d'obtenir le résultat de l'addition de deux instances de
 * Coord.
 *
 * On fait appel à la méthode operator+= pour une copie temporaire.
 *
 * @param c instance à additionner à l'instance courante.
 * @return une nouvelle instance résultant de l'addition des deux
 * instances.
 */
Coord Coord::operator+(const Coord &c) const
{
    Coord tmp( *this );
    return tmp += c;
}

/**
 * Permet d'additionner une instance de Coord à l'instance courante.
 *
 * @param c instance à additionner à l'instance courante.
 * @return une référence sur l'instance courante modifiée.
 */
Coord &Coord::operator+=(const Coord &c)
{
    this -> VecteurBase::operator+=( c );
    /*
  for (unsigned int i(0); i < 3; i++)
    coord[ i ] += c.coord[ i ];
  */
    return *this;
}

/**
 * Permet d'obtenir le résultat de la soustraction de deux instances
 * de Coord.
 *
 * On fait appel à la méthode operator-= pour une copie temporaire.
 *
 * @param c instance à soustraire à l'instance courante.
 * @return une nouvelle instance résultant de la soustraction des deux
 * instances.
 */
Coord Coord::operator-(const Coord &c) const
{
    Coord tmp( *this );
    return tmp -= c;
}

/**
 * Permet de soustraire une instance de Coord à l'instance courante.
 *
 * @param c instance à soustraire à l'instance courante.
 * @return une référence sur l'instance courante modifiée.
 */
Coord &Coord::operator-=(const Coord &c)
{
    this -> VecteurBase::operator+=( c );
    /*
  for (unsigned int i(0); i < 3; i++)
    coord[ i ] -= c.coord[ i ];
  */
    return *this;
}

/**
 * Permet de multiplier l'instance courante par un scalaire.
 *
 * @param a nombre réel par lequel on veut multiplier l'instance
 * courante.
 * @return une référence sur l'instance courante modifiée.
 */
Coord &Coord::operator*=(const double &a)
{
    this -> VecteurBase::operator*=( a );
    /*
  for (unsigned int i(0); i < 3; i++)
    coord[ i ] *= a;
  */
    return *this;
}

/**
 * Permet d'obtenir le résultat de la multiplication de l'instance
 * courante par un scalaire.
 *
 * Fait appel à la méthode operator*= pour une instance temporataire.
 *
 * @param a nombre réel par lequel on veut multiplier l'instance
 * courante.
 * @return une nouvelle instance résultant de la multiplication de
 * l'instance courante par le nombre réel donné.
 */
Coord Coord::operator*(const double &a) const
{
    Coord tmp( *this );

    return tmp *= a;
}

/**
 * Permet de diviser l'instance courante par un scalaire.
 *
 * @param scalaire nombre par lequel on veut diviser l'instance
 * courante.
 * @return une référence sur l'instance courante modifiée.
 */
Coord &Coord::operator/=(const double& scalaire)
{
    this -> operator*=( 1. / scalaire );
    /*
  for(unsigned int i(0); i < 3; i++)
    coord[ i ] /= scalaire;
  */
    return *this;
}

/**
 * Permet d'obtenir le résultat de la division de l'instance courante
 * par un scalaire.
 *
 * Fait appel à la méthode operator/= pour une instance temporataire.
 *
 * @param scalaire nombre réel par lequel on veut diviser l'instance
 * courante.
 * @return une nouvelle instance résultant de la division de
 * l'instance courante par le nombre réel donné.
 */
Coord Coord::operator/(const double& scalaire) const
{
    Coord tmp( *this );

    return tmp /= scalaire;
}

/**
 * Fonction amie permettant d'écrire <tt>pt * a</tt> ou <tt>a *
 * pt</tt> indifféremment.
 *
 * Fait appel à operator*.
 *
 * @param scalaire nombre réel par lequel on veut multiplier
 * l'instance de Coord.
 * @param pt instance de Coord que l'on veut multiplier par scalaire.
 * @return une nouvelle instance de Coord, résultant de la
 * multiplication de pt par scalaire.
 */
Coord operator*(const double& scalaire, const Coord& pt)
{
    return pt * scalaire;
}

/**
 * Opérateur de comparaison, utilisé pour pouvoir définir un conteneur
 * associatif dont la clef est une instance de Coord (map, set, ...).
 *
 * Ne doit pas être utilisé pour comparer deux instances!
 *
 * Est basé sur la comparaison des normes.
 *
 * @param autre instance à comparer avec l'instance courante.
 * @return \e true si la norme de l'instance courante est plus petite
 * que celle de autre.
 */
bool Coord::operator<(const Coord& autre) const
{
    return ( this -> r() < autre.r() );
}

/**
 * Fonction amie réalisant l'affichage des coordonnées sur un flot de
 * sortie quelconque.
 *
 * @param os référence sur le flot de sortie à utiliser.
 * @param pt instance de Coord à afficher.
 * @return une référence sur le flot de sortie modifié.
 */
ostream& operator<<(ostream& os, const Coord& pt)
{
    if ( pt.estInfini() )
        return os << "nulle part";
    else if ( ! pt.estDefini() )
        return os << "-- -- --";
    else
        return os << pt.x() << ' ' << pt.y() << ' ' << pt.z();
}
